package hied.learning;

import hied.AvgDev;
import hied.WordPositions;

import java.util.HashMap;
import java.util.Map;

public class WordPositionsCounter {
	private Map<Float, Integer> absStart = new HashMap<Float, Integer>();
	private Map<Float, Integer> absMiddle = new HashMap<Float, Integer>();
	private Map<Float, Integer> absEnd = new HashMap<Float, Integer>();
	private Map<Float, Integer> relStart = new HashMap<Float, Integer>();
	private Map<Float, Integer> relMiddle = new HashMap<Float, Integer>();
	private Map<Float, Integer> relEnd = new HashMap<Float, Integer>();

	public void addPosition(float position, float length) {
		this.addToCounter(absStart, position);
		this.addToCounter(absMiddle, position - (length / 2));
		this.addToCounter(absEnd, length - position);
		this.addToCounter(relStart, position / length);
		this.addToCounter(relMiddle, (position / length) - 0.5f);
		this.addToCounter(relEnd, 1 - (position / length));
	}

	private void addToCounter(Map<Float, Integer> map, Float position) {
		Integer count = map.get(position);
		if (count == null) {
			map.put(position, 1);
		} else {
			map.put(position, count++);
		}
	}

	public WordPositions getWordPositions() {
		AvgDev aStart = this.calculateAvgDev(absStart);
		AvgDev aMiddle = this.calculateAvgDev(absMiddle);
		AvgDev aEnd = this.calculateAvgDev(absEnd);
		AvgDev rStart = this.calculateAvgDev(relStart);
		AvgDev rMiddle = this.calculateAvgDev(relMiddle);
		AvgDev rEnd = this.calculateAvgDev(relEnd);

		return new WordPositions(aStart, aMiddle, aEnd, rStart, rMiddle, rEnd);
	}

	private AvgDev calculateAvgDev(Map<Float, Integer> map) {
		float sum = 0;
		float count = 0;
		float avg;
		float stDev;
		for (Float pos : map.keySet()) {
			Integer occ = map.get(pos);
			sum += occ * pos;
			count += occ;
		}
		avg = sum / count;
		sum = 0;
		for (Float pos : map.keySet()) {
			Integer occ = map.get(pos);
			sum += occ * (pos - avg) * (pos - avg);
		}
		if (count == 1) {
			return new AvgDev(avg, 0);
		}
		stDev = (float) Math.sqrt(sum / (count - 1));
		return new AvgDev(avg, stDev);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		this.addStringFromMap(sb, this.absStart);
		sb.append("\n");
		this.addStringFromMap(sb, this.absMiddle);
		sb.append("\n");
		this.addStringFromMap(sb, this.absEnd);
		sb.append("\n");
		this.addStringFromMap(sb, this.relStart);
		sb.append("\n");
		this.addStringFromMap(sb, this.relMiddle);
		sb.append("\n");
		this.addStringFromMap(sb, this.relEnd);
		return sb.toString();
	}

	private void addStringFromMap(StringBuilder sb, Map<Float, Integer> map) {
		sb.append("[");
		for (Float pos : map.keySet()) {
			Integer val = map.get(pos);
			sb.append("<");
			sb.append(pos);
			sb.append(":");
			sb.append(val);
			sb.append(">");
		}
		sb.append("]");
	}

}
